/*
 * Copyright (C) 2012 Tapas Mobile Ltd.  All Rights Reserved.
 */

package com.tools.cleanmaster.taskmain;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.text.TextUtils;

import com.tools.cleanmaster.CleanConstant;
import com.tools.cleanmaster.appinfo.AppInfoCompat;
import com.tools.cleanmaster.appinfo.AppManagerCompat;
import com.tools.cleanmaster.utils.FeatureConfig;
import com.tools.cleanmaster.utils.LogHelper;
import com.tools.cleanmaster.utils.PackageUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class AppTaskUtils {
    public static abstract class AppAsyncTask<T, V, S> extends AsyncTask<T, V, S> {
        public void doPublishProgress(V... values) {
            publishProgress(values);
        }

        public abstract void setTotal(int total);
    }

    private static final String AUTO_PROTECTED_LIST_FILE_NAME = "process_white_list.txt";
    private static final String SYS_PROTECTED_LIST_FILE_NAME = "system_white_list.txt";

    private static final boolean DEBUG = FeatureConfig.DEBUG_LOG;
    private static final String TAG = "TKListMgrBase";

    private static final String[] ONEKEY_PROTECTED_LIST = {
            "com.android.providers.", "system", "com.android.phone", "android",
            "com.android.systemui", "com.android.defcontainer", "com.google.android.gsf.login",
            "com.google.android.partnersetup", "com.google.android.backuptransport"
    };

    private static AppTaskUtils sInstance;
    private static ArrayList<String> sHomeSet = new ArrayList<String>();

    private AppTaskUtils(Context context) {
    }

    private static final Set<String> AUTO_PROTECTED_LIST = new HashSet<String>();

    private static final Set<String> SYS_PROTECTED_LIST = new HashSet<String>();

    public synchronized static AppTaskUtils getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppTaskUtils(context);
        }
        return sInstance;
    }

    public static String getPackageName(RunningAppProcessInfo amProcess) {
        String pname = amProcess.processName;
        String pkgName = null;
        if (amProcess.pkgList != null) {
            for (String s : amProcess.pkgList) {
                pkgName = s;
                // System.out.println("find pkg(" + pname + ") === " + s);
                if (s.equals(pname)) {
                    return pname;
                }
            }
        }
        if (pkgName == null) {
            pkgName = pname;
        }
        return pkgName;
    }

    public static List<ProcessItem> getRunningTaskList(Context ctx, ActivityManager mAm,
                                                       HashMap<String, ProcessItem> mProcessMap, boolean loadIcon,
                                                       boolean isAuto,
                                                       AppAsyncTask<Void, ProcessItem, Void> task) {
        long mTimeStart = SystemClock.elapsedRealtime();
        List<ProcessItem> mSelectedProcessList = new ArrayList<ProcessItem>();
        List<RunningAppProcessInfo> runProcessList = mAm.getRunningAppProcesses();

        if (runProcessList == null) {
            return mSelectedProcessList;
        }

        if (task != null) {
            task.setTotal(runProcessList.size());
        }

        for (RunningAppProcessInfo amProcess : runProcessList) {
            String pkgName = AppTaskUtils.getPackageName(amProcess);
            if (isProtected(pkgName)) {
                continue;
            }
            ProcessItem process = mProcessMap.get(pkgName);

            if (process == null) {
                if (DEBUG) {
                    LogHelper.d(TAG, "checking package " + pkgName + " : " + amProcess.uid);
                }
                if (!AppTaskUtils.checkRunningProcess(pkgName, amProcess.uid)) {
                    if (task != null) {
                        task.doPublishProgress();
                    }
                    continue;
                }

                process = new ProcessItem();
                if (loadIcon) {
                    AppInfoCompat appInfo = AppManagerCompat.getAppInfoNoFail(pkgName);
                    process.init(appInfo, amProcess);
                } else {
                    process.init(pkgName, amProcess);
                    process.sys = PackageUtils.isSystemApp(ctx, pkgName);
                }
                if (CleanConstant.REAL_PACKAGE_NAME.equals(pkgName)) {
                    process.checked = false;
                    process.isSelf = true;
                    process.isSuggestedProtected = true;
                }
                if (process.sys && isSysProtected(pkgName)) {
                    process.checked = false;
                }

                if (DEBUG) {
                    LogHelper.d(TAG, "package " + pkgName + ":" + process.foreground + " mark? "
                            + process.checked + " : " + sHomeSet.contains(pkgName));
                }
            }

            process.addPid(amProcess.pid);
            mProcessMap.put(pkgName, process);
            process.uid = amProcess.uid;
            if (!loadIcon || process.isValid()) {
                if (task != null) {
                    task.doPublishProgress(process);
                }
                getProcess(process, mSelectedProcessList);
            } else {
                if (FeatureConfig.DEBUG_LOG) {
                    LogHelper.w(TAG, "Bad process: " + pkgName + ", label: " + process.label
                            + ", icon: " + process.icon);
                }
            }
        }

        List<RunningServiceInfo> runServiceList = mAm.getRunningServices(100);

        if (runServiceList != null) {
            for (RunningServiceInfo amService : runServiceList) {
                if (!amService.started || amService.restarting > 0) {
                    // Skip services which are not started or are restarting
                    continue;
                }
                String pkgName = amService.service.getPackageName();
                /*if (CleanConstant.PSAFE_FILTER && TextUtils.equals(CleanConstant.PSAFE_PKGNAME, pkgName)) {
                    continue;
                }*/
                if (isProtected(pkgName)) {
                    continue;
                }
                ProcessItem process = mProcessMap.get(pkgName);
                if (process == null) {
                    if (DEBUG) {
                        LogHelper.d(TAG, "checking package " + pkgName + " : " + amService.uid);
                    }
                    if (!AppTaskUtils.checkRunningProcess(pkgName, amService.uid)) {
                        continue;
                    }

                    process = new ProcessItem();
                    if (loadIcon) {
                        AppInfoCompat appInfo = AppManagerCompat.getAppInfoNoFail(pkgName);
                        process.init(appInfo, amService);
                    } else {
                        process.init(pkgName, amService);
                        process.sys = PackageUtils.isSystemApp(ctx, pkgName);
                    }
                    if (process.sys && isSysProtected(pkgName)) {
                        process.checked = false;
                    }
                    if (DEBUG) {
                        LogHelper.d(TAG, "package " + pkgName + " mark? " + process.checked);
                    }
                }

                process.addPid(amService.pid);
                mProcessMap.put(pkgName, process);
                if (!loadIcon || process.isValid()) {
                    if (task != null) {
                        task.doPublishProgress(process);
                    }
                    getProcess(process, mSelectedProcessList);
                } else {
                    if (FeatureConfig.DEBUG_LOG) {
                        LogHelper.w(TAG, "Bad service: " + pkgName + ", label: " + process.label
                                + ", service: " + amService.service);
                    }
                }
            }
        }
        if (DEBUG) {
            long timeSnap = SystemClock.elapsedRealtime() - mTimeStart;
            LogHelper.d(TAG, "getRunningTaskList takes: " + timeSnap + "ms");
        }
        return mSelectedProcessList;
    }

    private static void getProcess(ProcessItem process, List<ProcessItem> mProcessList) {

        if (!mProcessList.contains(process)) {
            if (process.checked) {
                mProcessList.add(process);
            }
        }
    }

    public static boolean checkRunningProcess(String pkgName, int uid) {
        if (uid < 10000) {
            // if user id less than 10000, mean system uid, we should skip it.
            return false;
        }
        if (AppTaskUtils.isOneKeyProtected(pkgName)) {
            return false;
        }
        return !CleanConstant.REAL_PACKAGE_NAME.equals(pkgName);
    }

    public static boolean isOneKeyProtected(String pkgName) {
        for (String item : ONEKEY_PROTECTED_LIST) {
            if (pkgName.equals(item) || (item.endsWith(".") && pkgName.startsWith(item))) {
                return true;
            }
        }
        return false;
    }

    public static void loadAutoProtectedList() {
        synchronized(AUTO_PROTECTED_LIST) {
            if (AUTO_PROTECTED_LIST.size() > 0) {
                return;
            }
            AUTO_PROTECTED_LIST.addAll(Arrays.asList(ConfigUtils.loadArrayFromFile(AUTO_PROTECTED_LIST_FILE_NAME)));
        }
    }

    public static void loadSysProtectedList() {
        synchronized(SYS_PROTECTED_LIST) {
            if (SYS_PROTECTED_LIST.size() > 0) {
                return;
            }
            SYS_PROTECTED_LIST.addAll(Arrays.asList(ConfigUtils.loadArrayFromFile(SYS_PROTECTED_LIST_FILE_NAME)));
        }
    }

    public static boolean isProtected(String pkgName) {
        if (TextUtils.isEmpty(pkgName)) {
            return false;
        }
        if (AUTO_PROTECTED_LIST.contains(pkgName)) {
            return true;
        }
        boolean isProtectedByPattern = false;
        String[] segments = pkgName.split("\\.");
        int length = segments.length;
        StringBuilder tempPackageName = new StringBuilder();
        if (length > 1) {
            for (int i = 0; i < length - 1; i++) {
                tempPackageName.append(segments[i]).append(".");
                if (AUTO_PROTECTED_LIST.contains(tempPackageName.toString())) {
                    isProtectedByPattern = true;
                    break;
                }
            }
        }
        return false;//todo 不要提交
    }

    public static boolean isSysProtected(String pkgName) {
        return SYS_PROTECTED_LIST.contains(pkgName);
    }
}
